package com.eobard.obfuscate.optional;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.eobard.config.obfuscate.ObfuscatorConfig;
import com.eobard.enums.MixMD5Enum;
import com.eobard.enums.PyDependenciesEnum;
import com.eobard.obfuscate.IObfuscator;
import com.eobard.params.AlterMD5Params;
import com.eobard.utils.CollectionUtils;
import com.eobard.utils.EnvUtils;
import com.eobard.utils.FileUtils;
import com.eobard.utils.PythonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @Author: Eobard Gu
 * @Date: 2025-07-31
 * @Description: 修改音频文件、图片文件MD5核心类
 */
@Component
public class AlterMD5Obfuscator implements IObfuscator {

    @Autowired
    private Executor threadPoolExecutor;

    //资源文件的根路径
    private static String resourceRoot;

    //音频文件所在的文件名称
    private List<String> audioSourceDirs = null;
    //音频文件修改md5后保存的路径(如果不设置,默认保存到源路径里面)
    private List<String> audioSaveTargetDirs = null;

    //图片文件所在的文件名称
    private List<String> imageSourceDirs = null;
    //图片文件修改md5后保存的路径(如果不设置,默认保存到源路径里面)
    private List<String> imageSaveTargetDirs = null;


    //初始化数据和检查
    @Override
    public void initialize(ObfuscatorConfig config) {
        //清空上一次记录
        CollectionUtils.clear(audioSourceDirs, audioSaveTargetDirs, imageSourceDirs, imageSaveTargetDirs);
        //获取配置参数
        AlterMD5Params md5Params = config.getAlterMD5Params();
        //获取资源文件根路径
        String path = config.getResourceRoot();
        resourceRoot = path.endsWith(File.separator) ? path.substring(0, path.lastIndexOf(File.separator)) : path;

        //获取所有输入文件夹进行检查,看看是否符合要求
        List<String> dirs = Stream.of(md5Params.getAudioSourceDirs(), md5Params.getImageSourceDirs())
                .filter(Objects::nonNull)
                .flatMap(List::stream)
                .distinct()
                .collect(Collectors.toList());

        //检查文件夹是否符合要求
        FileUtils.checkDirectoryExist(dirs, resourceRoot);

        //原始需要重写MD5的资源文件
        audioSourceDirs = buildFullPath(md5Params.getAudioSourceDirs());
        imageSourceDirs = buildFullPath(md5Params.getImageSourceDirs());
        //检查文件夹是否符合文件类型要求
        FileUtils.checkFileExt(audioSourceDirs, FileUtils.AUDIO);
        FileUtils.checkFileExt(imageSourceDirs, FileUtils.IMAGE);

        //目标保存路径
        audioSaveTargetDirs = md5Params.getAudioSaveTargetDirs();
        imageSaveTargetDirs = md5Params.getImageSaveTargetDirs();
    }

    //混淆核心代码
    @Override
    public void obfuscate(ObfuscatorConfig config) {
        //1.如果有音频输入路径,那么就要执行音频的MD5值更改
        threadPoolExecutor.execute(() -> processMd5Rewrite(audioSourceDirs, audioSaveTargetDirs, MixMD5Enum.AUDIO));
        //2.如果有图片输入路径,那么就要执行图片的MD5值更改
        threadPoolExecutor.execute(() -> processMd5Rewrite(imageSourceDirs, imageSaveTargetDirs, MixMD5Enum.IMAGE));
    }

    //是否启用这个功能
    @Override
    public boolean supports(ObfuscatorConfig config) {
        return ObjectUtil.isNotEmpty(config.getAlterMD5Params());
    }


    /**
     * 将sourceDirs的文件通过md5脚本全部保存到saveTargetDirs中
     *
     * @param sourceDirs
     * @param saveTargetDirs
     * @param type:MD5修改的类型
     */
    private void processMd5Rewrite(List<String> sourceDirs, List<String> saveTargetDirs, MixMD5Enum type) {
        //检查依赖是否存在
        List<String> dep = type == MixMD5Enum.AUDIO ? PyDependenciesEnum.AUDIO.dependencies : PyDependenciesEnum.IMAGE.dependencies;
        EnvUtils.hasPythonDependencies(dep);

        if (CollectionUtil.isEmpty(sourceDirs)) return;
        //如果保存路径为空,那么默认保存到源路经
        if (CollectionUtil.isEmpty(saveTargetDirs)) {
            //参数2:源地址,参数3:修改md5值后保存的目标地址
            sourceDirs.forEach(path -> PythonUtils.run(type.fileName, path, path));
        } else {
            //源地址大小
            int sSize = sourceDirs.size();
            //目标地址大小
            int tSize = saveTargetDirs.size();
            //情况1:源地址==目标地址:那么一一对应修改并保存源地址到目标地址
            if (sSize == tSize) {
                IntStream.range(0, sSize).forEach(i ->
                        PythonUtils.run(type.fileName, sourceDirs.get(i), saveTargetDirs.get(i)));
                //情况2:源地址>目标地址 & 目标地址只有一个:那么所有源地址的文件全部保存到目标地址
            } else if (tSize == 1) {
                sourceDirs.forEach(src -> PythonUtils.run(type.fileName, src, saveTargetDirs.get(0)));
            } else {
                throw new RuntimeException(String.format("%sSourceDirs 的路径只能一一对应 %sSaveTargetDirs，或多个 source 只能保存到一个 target 中"));
            }
        }
    }

    //构建完整路径: resourceRoot/文件夹名称
    private List<String> buildFullPath(List<String> dir) {
        if (CollectionUtil.isEmpty(dir)) return null;
        return dir.stream().map(d -> {
            if (!d.startsWith(File.separator)) {
                return resourceRoot + File.separator + d;
            }
            return resourceRoot + d;
        }).collect(Collectors.toList());
    }
}
